from server import vfs
from aiofiles import base as aiofiles_base
from aiofiles import threadpool as aiofiles_threadpool
import os
from server.login import User
import asyncio
from typing import Iterator, Callable, Any, Iterable, TypeVar, Union, Literal, overload
from concurrent.futures import ThreadPoolExecutor


async def mkthread(func, *args, **kwargs):  # 创建线程并处理异常
    def new_thread():
        try:
            return func(*args, **kwargs)
        except Exception as e:  # 处理 to_thread 异常无法正常抛出问题
            return e
    ret = await asyncio.to_thread(new_thread)
    if (isinstance(ret, Exception)):
        raise ret
    return ret


class _open_obj_base(aiofiles_base.AiofilesContextManager):  # 打开文件对象
    def __init__(self, user: User, path: str, mode: str, fb) -> None:
        self.user = user
        self.path = path
        self.mode = mode
        super().__init__(fb)

    async def __aenter__(self):
        abspath = os.path.abspath(self.path)
        ret = await vfs.get_permission(self.user.username, abspath)
        if ("w" in self.mode or "a" in self.mode):
            if (ret < 2):  # 写入权限
                raise PermissionError("Permission denied")
        else:
            if (ret < 1):  # 读取权限
                raise PermissionError("Permission denied")
        return await super().__aenter__()


class _open_obj_text(_open_obj_base):
    def __init__(self, user: User, path: str, mode: Literal["r", "w", "a"], fb) -> None:
        super().__init__(user, path, mode, fb)

    async def __aenter__(self) -> aiofiles_threadpool.text.AsyncTextIOWrapper:
        return await super().__aenter__()


class _open_obj_bin(_open_obj_base):
    def __init__(self, user: User, path: str, mode: Literal["rb", "wb", "ab"], fb) -> None:
        super().__init__(user, path, mode, fb)

    async def __aenter__(self) -> aiofiles_threadpool.binary.AsyncBufferedIOBase:
        return await super().__aenter__()


class _open_obj_plus(_open_obj_base):
    def __init__(self, user: User, path: str, mode: Literal["r+", "w+", "a+", "rb+", "wb+", "ab+"], fb) -> None:
        super().__init__(user, path, mode, fb)

    async def __aenter__(self) -> aiofiles_threadpool.binary.AsyncBufferedReader:
        return await super().__aenter__()


OpenModes = Union[Literal["r", "w", "a"], Literal["rb", "wb",
                                                  "ab"], Literal["r+", "w+", "a+", "rb+", "wb+", "ab+"]]


def fix(func):  # 处理函数参数
    def warpper(self, *args, **kwargs):
        return func(*args, **kwargs)
    return warpper


T = TypeVar('T')


class OS:
    r"""OS routines for NT or Posix depending on what system we're on.

    This exports:
    - all functions from posix or nt, e.g. unlink, stat, etc.
    - os.path is either posixpath or ntpath
    - os.name is either 'posix' or 'nt'
    - os.curdir is a string representing the current directory (always '.')
    - os.pardir is a string representing the parent directory (always '..')
    - os.sep is the (or a most common) pathname separator ('/' or '\\')
    - os.extsep is the extension separator (always '.')
    - os.altsep is the alternate pathname separator (None or '/')
    - os.pathsep is the component separator used in $PATH etc
    - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
    - os.defpath is the default search path for executables
    - os.devnull is the file path of the null device ('/dev/null', etc.)

    Programs that import and use 'os' stand a better chance of being
    portable between different platforms.  Of course, they must then
    only use functions that are defined by all platforms (e.g., unlink
    and opendir), and leave all pathname manipulation to os.path
    (e.g., split and join).
    """

    modetext = aiofiles_threadpool.text.AsyncTextIOWrapper
    modebin = aiofiles_threadpool.binary.AsyncBufferedIOBase
    modeplus = aiofiles_threadpool.binary.AsyncBufferedReader

    def __init__(self, user: User):
        self.user = user  # 用户
        self.path = _path(user)

    async def run(self, func: Callable[..., T], *args, **kwargs) -> T:
        return await mkthread(func, *args, **kwargs)

    async def listdir(self, path: str) -> list[str]:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.listdir, path)

    async def remove(self, path: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.remove, path)

    unlink = remove

    @overload
    def open(self,
             file: str,
             mode: Literal["r", "w", "a"],
             buffering: int = -1,
             encoding: None | str = None,
             errors: str | None = None,
             newline: str | None = None,
             closefd: bool = True,
             opener: str | None = None,
             *,
             loop: asyncio.EventLoop | None = None,
             executor: ThreadPoolExecutor | None = None) -> _open_obj_text:
        ...

    @overload
    def open(self,
             file: str,
             mode: Literal["rb", "wb", "ab"],
             buffering: int = -1,
             encoding: None | str = None,
             errors: str | None = None,
             newline: str | None = None,
             closefd: bool = True,
             opener: str | None = None,
             *,
             loop: asyncio.EventLoop | None = None,
             executor: ThreadPoolExecutor | None = None) -> _open_obj_bin:
        ...

    @overload
    def open(self,
             file: str,
             mode: Literal["r+", "w+", "a+", "rb+", "wb+", "ab+"],
             buffering: int = -1,
             encoding: None | str = None,
             errors: str | None = None,
             newline: str | None = None,
             closefd: bool = True,
             opener: str | None = None,
             *,
             loop: asyncio.EventLoop | None = None,
             executor: ThreadPoolExecutor | None = None) -> _open_obj_plus:
        ...

    def open(self,
             file,
             mode="r",
             buffering=-1,
             encoding=None,
             errors=None,
             newline=None,
             closefd=True,
             opener=None,
             loop=None,
             executor=None):
        return _open_obj_base(self.user, file, mode,
                              aiofiles_threadpool._open(  # type:ignore
                                  file,
                                  mode=mode,
                                  buffering=buffering,
                                  encoding=encoding,
                                  errors=errors,
                                  newline=newline,
                                  closefd=closefd,
                                  opener=opener,
                                  loop=loop,
                                  executor=executor,
                              ))

    async def rename(self, src: str, dst: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(src))
        if (ret < 2):
            raise PermissionError("Permission denied")
        ret = await vfs.get_permission(self.user.username, os.path.abspath(dst))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.rename, src, dst)

    async def stat(self, path: str, follow_symlinks=True) -> os.stat_result:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.stat, path, follow_symlinks=follow_symlinks)

    async def link(self, src: str, dst: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(dst))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.link, src, dst)

    async def symlink(self, src: str, dst: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(dst))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.symlink, src, dst)

    async def mkdir(self, path: str, mode: int = 0o777) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.mkdir, path, mode=mode)

    async def makedirs(self, path: str, mode: int = 0o777, exist_ok: bool = False) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.makedirs, path, mode=mode, exist_ok=exist_ok)

    async def rmdir(self, path: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.rmdir, path)

    async def removedirs(self, name: str) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(name))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.removedirs, name)

    async def chmod(self, path: str, mode: int, follow_symlinks: bool = True) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.chmod, path, mode, follow_symlinks=follow_symlinks)

    async def chown(self, path: str, uid: int, gid: int, follow_symlinks: bool = True) -> None:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 2):
            raise PermissionError("Permission denied")
        return await mkthread(os.chown, path, uid, gid, follow_symlinks=follow_symlinks)

    sep = os.sep
    linesep = os.linesep
    pathsep = os.sep
    devnull = os.devnull
    F_OK = os.F_OK
    R_OK = os.R_OK
    W_OK = os.W_OK
    X_OK = os.X_OK
    altsep = os.altsep
    defpath = os.defpath

    async def access(self, path: str, mode: int, *, follow_symlinks: bool = True) -> bool:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        if (mode == os.W_OK):
            if (ret < 2):
                return False
        return await mkthread(os.access, path, mode, follow_symlinks=follow_symlinks)

    async def walk(self, top: str, topdown: bool = True, followlinks: bool = False) -> Iterator:
        ret = await vfs.get_permission(self.user.username, os.path.abspath(top))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.walk, top, topdown=topdown, followlinks=followlinks)

    async def scandir(self, path: str | None, follow_symlinks: bool = True) -> None:
        if (path == None):
            path = os.getcwd()
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.scandir, path, follow_symlinks=follow_symlinks)


class _path():
    """OS routines for NT or Posix depending on what system we're on.

This exports:
all functions from posix or nt, e.g. unlink, stat, etc.
os.path is either posixpath or ntpath
os.name is either 'posix' or 'nt'
os.curdir is a string representing the current directory (always '.')
os.pardir is a string representing the parent directory (always '..')
os.sep is the (or a most common) pathname separator ('/' or '\')
os.extsep is the extension separator (always '.')
os.altsep is the alternate pathname separator (None or '/')
os.pathsep is the component separator used in $PATH etc
os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
os.defpath is the default search path for executables
os.devnull is the file path of the null device ('/dev/null', etc.)

Programs that import and use 'os' stand a better chance of being
portable between different platforms. Of course, they must then
only use functions that are defined by all platforms (e.g., unlink
and opendir), and leave all pathname manipulation to os.path
(e.g., split and join)."""

    def __init__(self, user: User):
        self.user = user

    async def exists(self, path: str) -> bool:
        """Test whether a path exists. Returns False for broken symbolic links"""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.exists, path)

    async def isdir(self, path: str) -> bool:
        """Return true if the pathname refers to an existing directory."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.isdir, path)

    async def isfile(self, path: str) -> bool:
        """Test whether a path is a regular file"""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.isfile, path)

    async def islink(self, path: str) -> bool:
        """Test whether a path is a symbolic link"""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.islink, path)

    async def ismount(self, path: str) -> bool:
        """Test whether a path is a mount point"""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.ismount, path)

    async def isabs(self, path: str) -> bool:
        """Test whether a path is absolute"""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.isabs, path)

    async def getatime(self, path: str) -> float:
        """Return the last access time of a file, reported by os.stat()."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.getatime, path)

    async def getmtime(self, path: str) -> float:
        """Return the last modification time of a file, reported by os.stat()."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.getmtime, path)

    async def getctime(self, path: str) -> float:
        """Return the metadata change time of a file, reported by os.stat()."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.getctime, path)

    async def getsize(self, path: str) -> int:
        """Return the size of a file, reported by os.stat()."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(path))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.getsize, path)

    async def getcwd(self) -> str:
        """Return a string representing the current working directory"""
        return await mkthread(os.getcwd)

    async def samefile(self, f1: str, f2: str) -> bool:
        """
Test whether two pathnames reference the same actual file or directory

This is determined by the device number and i-node number and
raises an exception if an os.stat() call on either pathname fails."""
        ret = await vfs.get_permission(self.user.username, os.path.abspath(f1))
        if (ret < 1):
            raise PermissionError("Permission denied")
        ret = await vfs.get_permission(self.user.username, os.path.abspath(f2))
        if (ret < 1):
            raise PermissionError("Permission denied")
        return await mkthread(os.path.samefile, f1, f2)

    samestat: Callable[[Any, Any], str] = fix(os.path.samestat)
    sameopenfile: Callable[[int, int], str] = fix(os.path.sameopenfile)
    realpath: Callable[[str], str] = fix(os.path.realpath)
    normpath: Callable[[str], str] = fix(os.path.normpath)
    noempase: Callable[[str], str] = fix(os.path.normcase)
    commonpath: Callable[[Iterable[str]], str] | Callable[[
        Iterable[bytes]], bytes] = fix(os.path.commonpath)
    commonperfix = fix(os.path.commonprefix)
    basename: Callable[[str], str] = fix(os.path.basename)
    dirname: Callable[[str], str] = fix(os.path.dirname)
    join: Callable[..., str] = fix(os.path.join)
    split: Callable[[str], str] = fix(os.path.split)
    splitdrive: Callable[[str], str] = fix(os.path.splitdrive)
    splitext: Callable[[str], str] = fix(os.path.splitext)
    abspath: Callable[[str], str] = fix(os.path.abspath)
